#include "drivers.h"
#include "transmit.h"
#include "vehicle.h"

//#define USE_HTTP_CONNECTION   1

// connect sequence:
//      ATE0
//      AT+CPIN?
//      AT+CREG?
//      AT+CGATT?
//      AT+ZIPCFG=CMNET
//      AT+ZIPCALL=1
//      AT+GTPOS=1
// connect cmd
//      AT+ZIPOPEN=2,0,192.168.1.1,8880
// connect location server
//      AT+GTPOS=1
// get location info
//      AT+GTPOS=2

#define AT_CR                   '\r'

#define L206_RETRY_TIMES        8

#define KEYPOWER_PORT           GPIOB
#define KEYPOWER_PIN            GPIO_Pin_0
#define KEYPOWER_RCC_APB        RCC_APB2Periph_GPIOB

#define RESET_PORT              GPIOC
#define RESET_PIN               GPIO_Pin_15
#define RESET_RCC_APB           RCC_APB2Periph_GPIOC

#define KEYPOWER_1()            GPIO_ResetBits(KEYPOWER_PORT, KEYPOWER_PIN)
#define KEYPOWER_0()            GPIO_SetBits(KEYPOWER_PORT, KEYPOWER_PIN)

#define RESET_0()               GPIO_ResetBits(RESET_PORT, RESET_PIN)
#define RESET_1()               GPIO_SetBits(RESET_PORT, RESET_PIN)

OS_TCB L206TaskTCB;
CPU_STK L206TaskStk[L206_TASK_STK_SIZE];

extern OS_TCB TransmitProcessTaskTCB;
OS_TMR l206_timer;
OS_MUTEX mMutex;
uint8_t mTimeoutFlag = 0;
uint8_t mConnected = FALSE;
uint8_t mConnectLocation = FALSE;
char l206_imei[15];
uint8_t mSuspend = FALSE;

uint8_t is_suspend(void)
{
    return mSuspend;
}

void l206_init(void)
{
    OS_ERR  err;  
    GPIO_InitTypeDef gpio;

    RCC_APB2PeriphClockCmd(KEYPOWER_RCC_APB, ENABLE);
    gpio.GPIO_Pin = KEYPOWER_PIN;
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(KEYPOWER_PORT, &gpio);

    RCC_APB2PeriphClockCmd(RESET_RCC_APB, ENABLE);
    gpio.GPIO_Pin = RESET_PIN;
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(RESET_PORT, &gpio);

    logi("%s done", __func__);

    OSTaskCreate((OS_TCB       *)&L206TaskTCB,  /* 任务控制块地址 */           
            (CPU_CHAR     *)"L206 TASK",  /* 任务名 */
            (OS_TASK_PTR   )l206_task,      /* 启动任务函数地址 */
            (void         *)0,                 /* 传递给任务的参数 */
            (OS_PRIO       )L206_TASK_PRIO, /* 任务优先级 */
            (CPU_STK      *)&L206TaskStk[0],     /* 堆栈基地址 */
            (CPU_STK_SIZE  )L206_TASK_STK_SIZE / 10, /* 堆栈监测区，这里表示后10%作为监测区 */
            (CPU_STK_SIZE  )L206_TASK_STK_SIZE,  /* 堆栈空间大小 */
            (OS_MSG_QTY    )0,  /* 本任务支持接受的最大消息数 */
            (OS_TICK       )0,  /* 设置时间片 */
            (void         *)0,  /* 堆栈空间大小 */  
            (OS_OPT        )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
            (OS_ERR       *)&err);
    //create timer
    OSTmrCreate((OS_TMR *)&l206_timer,
            (CPU_CHAR   *)"L206 TIMER",
            (OS_TICK     )OS_CFG_TMR_TASK_RATE_HZ * 4,
            (OS_TICK     )0,
            (OS_OPT      )OS_OPT_TMR_ONE_SHOT,
            (OS_TMR_CALLBACK_PTR)l206_timer_callback,
            (void       *)0,
            (OS_ERR     *)&err);
    //create mutex to protect the usart data in/output
    OSMutexCreate(
            (OS_MUTEX   *)&mMutex,
            (CPU_CHAR   *)"L206 MUTEX",
            &err
            );
    mSuspend = FALSE;
}

void l206_start_timer(uint16_t timeout)
{
    OS_ERR err;

    OSTmrSet((OS_TMR *)&l206_timer,
            (OS_TICK     )OS_CFG_TMR_TASK_RATE_HZ * timeout,
            (OS_TICK     )0,
            (OS_TMR_CALLBACK_PTR)l206_timer_callback,
            (void       *)0,
            (OS_ERR     *)&err);
    OSTmrStart((OS_TMR *)&l206_timer, &err);
    if(err != OS_ERR_NONE) {
        loge("start timer failed, err = %d", err);
    }
}

void l206_stop_timer(void)
{
    OS_ERR err;

    OSTmrStop((OS_TMR *)&l206_timer, OS_OPT_TMR_NONE, (void *)0, &err);
    if(err != OS_ERR_NONE) {
        loge("stop timer failed, err = %d", err);
    }
}

static void l206_timer_callback(void *p_arg)
{
    (void)p_arg;
    logi("%s", __func__);
    mTimeoutFlag = 1;
}

//l206_task:
//1. receive the remove server tcp socket
//2. combine the json and process
static void  l206_task(void *p_arg)
{
    OS_ERR err;
    static uint8_t rx_buf[256];
    static uint16_t rx_len = 0;
    static uint8_t retry = 0;
    uint8_t u_data;

    (void)p_arg;

    mConnected = FALSE;
    retry = 0;
    while(!mConnected && retry < L206_RETRY_TIMES) {
        if(l206_poweron() == TRUE) {
            l206_set_baudrate();
            l206_cgmr();
            l206_cgsn();
            l206_cpin();
            l206_iccid();
            l206_creg();
            l206_cgatt();
#ifdef USE_HTTP_CONNECTION
            if(l206_http_connect("www.baidu.com", 80)) {
                logi("connect baidu done");
                mConnected = TRUE;
                //l206_http_get();
            } else {
                loge("failed to connect to baidu");
            }
#else
            if(l206_connect("139.224.227.174", 8800)) {
                if(l206_location_init()) {
                    mConnectLocation = TRUE;
                    logi("location init success");
                } else {
                    loge("location init failed");
                }
                mConnected = TRUE;
            }
#endif
        }
        retry ++;
        if(!mConnected)
            l206_reset();
    }
    if(!mConnected) {
        loge("##network has problem, cannot connect to server");
        loge("##poweroff module");
        l206_poweroff();
        loge("##wait 30min to reboot");
        //set suspend flag
        mSuspend = TRUE;
        os_delay_min(30);
        software_reboot();
    }
    transmit_init();
    vehicle_init();
    while(1) {
        os_delay_ms(20);
#if 1
        while(comGetChar(COM_L206, &u_data)) {
            //printf("%c", u_data);
            if(u_data == '{') {
                rx_len = 0;
                memset(rx_buf, 0x00, 256);
            } else if(u_data == '}') {
                rx_buf[rx_len ++] = u_data;
                //get json buffer done
                //send buf to remote
                OSTaskQPost(
                        (OS_TCB     *) &TransmitProcessTaskTCB,
                        (void       *) rx_buf,
                        (OS_MSG_SIZE ) rx_len,
                        (OS_OPT      ) OS_OPT_POST_FIFO,
                        (OS_ERR     *) &err
                        );
            }
            rx_buf[rx_len ++] = u_data;
            if(rx_len >= 256)
                rx_len = 0;
        }
#endif
    }
}

void l206_reset(void)
{
    RESET_1();
    os_delay(2);
    RESET_0();
    os_delay(1);
    RESET_1();
    os_delay(2);
}

uint8_t l206_poweron(void)
{
    uint8_t i;

    for(i = 0; i < 1; i++) {
        l206_write("ATE0\r\n");
        if(l206_wait_rsp("OK", 1)) {
            logi("Already poweron!");
            return TRUE;
        } else {
        }
    }

    l206_reset();
    KEYPOWER_0();
    os_delay(2);
    KEYPOWER_1();
    os_delay(10);
    comClearRxFifo(COM_L206);
    for(i = 0; i < 5; i++) {
        l206_write("ATE0\r\n");
        if(l206_wait_rsp("OK", 1)) {
            logi("poweron success");
            return TRUE;
        }
    }
    loge("poweron failed");

    return FALSE;
}

void l206_poweroff(void)
{
    logi("%s", __func__);
    KEYPOWER_0();
    os_delay(2);
    KEYPOWER_1();
}

void l206_cgmr(void)
{
    comClearRxFifo(COM_L206);
    l206_write("AT+CGMR\r\n");
    if(l206_wait_rsp("OK", 3)) {
        logi("%s: check version success", __func__);
    } else {
        loge("%s: check version failed", __func__);
    }
}

uint8_t is_digital(char c)
{
    if(c >= '0' && c <= '9')
        return TRUE;
    else
        return FALSE;
}

void l206_set_baudrate(void)
{
    comClearRxFifo(COM_L206);
    l206_write("AT+IPR=115200\r\n");
    if(l206_wait_rsp("OK", 3)) {
        logi("%s: set baudrate success", __func__);
    } else {
        loge("%s: set baudrate failed", __func__);
    }
}

void l206_iccid(void)
{
    comClearRxFifo(COM_L206);
    l206_write("AT+ICCID\r\n");
    if(l206_wait_rsp("OK", 3)) {
        logi("%s: read iccid success", __func__);
    } else {
        loge("%s: read iccid failed", __func__);
    }
}

void l206_cgsn(void)
{
    char buf[20];
    uint8_t i;
    uint8_t offset;

    memset(buf, 0x00, 20);
    memset(l206_imei, 0x00, 15);
    comClearRxFifo(COM_L206);
    l206_write("AT+CGSN\r\n");
    if(l206_read_rsp("OK", buf, 3)) {
        logi("%s: check device imei success", __func__);
    } else {
        loge("%s: check device imei failed", __func__);
    }

    for(i = 0; i < 20; i++) {
        if(is_digital(buf[i])) {
            offset = i;
            break;
        }
    }
    if(i < 5) {
        memcpy(l206_imei, buf + offset, 15);
    }
    logi("imei: %s", l206_imei);
}

void l206_csq(void)
{
    comClearRxFifo(COM_L206);
    l206_write("AT+CSQ\r\n");
    if(l206_wait_rsp("OK", 1)) {
        logi("%s: check csq success", __func__);
    } else {
        loge("%s: check csq failed", __func__);
    }
}


uint8_t l206_cpin(void)
{
    uint8_t ret;

    comClearRxFifo(COM_L206);
    l206_write("AT+CPIN?\r\n");
    if(l206_wait_rsp("OK", 1)) {
        logi("%s: check sim success", __func__);
        ret = TRUE;
    } else {
        loge("%s: check sim failed", __func__);
        ret = FALSE;
    }
    return ret;
}

uint8_t l206_creg(void)
{
    uint8_t ret;
    uint8_t i;
    uint8_t status;
    char buf[32];
    char *sub_str;

    memset(buf, 0x00, 32);

    for(i = 0; i < 10; i++) {
        l206_csq();
        os_delay(1);
        comClearRxFifo(COM_L206);
        l206_write("AT+CREG?\r\n");
        if(l206_read_rsp("OK", buf, 3)) {
            logi("%s: register network success", __func__);
            ret = TRUE;
        } else {
            loge("%s: register network failed", __func__);
            ret = FALSE;
        }
        sub_str = strstr(buf, ",");
        status = sub_str[1] - '0';
        logi("status = %d", status);
        if(status == 1 || status == 5) {
            ret = TRUE;
            break;
        }
    }
    return ret;
}

uint8_t l206_cgatt(void)
{
    uint8_t ret;

    comClearRxFifo(COM_L206);
    l206_write("AT+CGATT?\r\n");
    if(l206_wait_rsp("OK", 3)) {
        logi("%s: cgatt success", __func__);
        ret = TRUE;
    } else {
        loge("%s: cgatt failed", __func__);
        ret = FALSE;
    }
    return ret;
}


uint8_t l206_location_init(void)
{
    uint8_t ret;

    comClearRxFifo(COM_L206);
    l206_write("AT+GTPOS=1\r\n");
    if(l206_wait_rsp("CONNECT OK", 6)) {
        logi("%s: gtpos success", __func__);
        ret = TRUE;
    } else {
        loge("%s: gtpos failed", __func__);
        ret = FALSE;
    }
    return ret;
}

uint8_t l206_send(uint8_t *buf, uint16_t len)
{
    uint8_t ret = FALSE;
    uint8_t *tx_buf;
    uint16_t index = 0, i;

    logi("%s: %s", __func__, buf);
    l206_lock();
    tx_buf = (uint8_t *)malloc(13 + 2 * len);
    memset(tx_buf, 0x00, 13 + 2 * len);
    index = snprintf((char *)tx_buf, 1024, "AT+ZIPSEND=2,");
    for(i = 0; i < len; i++) {
        index += snprintf((char *)tx_buf + index, 1024, "%02x", buf[i]);
    }
    l206_write((char *)tx_buf);
    l206_write("\r\n");
    if(l206_wait_rsp("SEND OK", 5)) {
        ret = TRUE;
    } else {
        loge("%s: send failed", __func__);
        ret = FALSE;
    }
    free(tx_buf);
    tx_buf = NULL;
    l206_unlock();

    return ret;
}

uint8_t l206_get_location(Location *location)
{
    uint8_t i;
    char *s, *p;
    char data[100];
    uint8_t buf[40];
    uint16_t r_size;
    double lng, lat;
    char lng_buf[20];
    char lat_buf[20];
    uint16_t index;

    memset(data, 0x00, 100);
    comClearRxFifo(COM_L206);
    l206_write("AT+GTPOS=2\r\n");

    r_size = l206_read_rsp("OK", data, 5);
    logi("data: %s, size = %d", data, r_size);
    if(r_size == 0) {
        l206_location_init();
        return FALSE;
    }

    if(NULL != (s = strstr(data, "+GTPOS:"))) {
        s = strstr((char *)(s), " ");
        s =s + 1;
        p = strstr((char *)(s), "\r\n");
        if(NULL != s) {
            i = 0;
            while(s < p) {
                buf[i++] = *(s++);
            }
            buf[i] = '\0';
        }
    } else {
        loge("cannot get position info, [%s]", data);
        //need to reconnect position server
        l206_location_init();
        return FALSE;
    }
    memset(lng_buf, 0x00, 20);
    memset(lat_buf, 0x00, 20);
    index = 0;
    i = 0;
    while(buf[i] != ',') {
        if(i >= 20) {
            loge("failed to parse location string");
            return FALSE;
        }
        lng_buf[index ++] = buf[i++];
    }
    lng_buf[index] = '\0';
    i++;
    index = 0;
    while(buf[i] != '\0') {
        lat_buf[index ++] = buf[i++];
    }
    lat_buf[index] = '\0';
    logi("lng = %s, lat = %s", lng_buf, lat_buf);
    lng = atof(lng_buf);
    lat = atof(lat_buf);
    if(lng > 180 || lat > 180) {
        loge("lng = %f, lat = %f", lng, lat);
        return FALSE;
    }
    //upload_location(lng * 1000000, lat * 1000000);
    location->longitude = lng * 100000;
    location->latitude = lat * 100000;

    return TRUE;
}

uint8_t l206_connect(const char *host, uint32_t port)
{
    uint8_t ret;
    uint8_t tx_buf[128];

    comClearRxFifo(COM_L206);
    l206_write("AT+ZIPCFG=unim2m.njm2mapn\r\n");
    if(l206_wait_rsp("OK", 8)) {
        logi("%s: register gprs success", __func__);
        ret = TRUE;
    } else {
        loge("%s: register gprs failed", __func__);
        ret = FALSE;
        goto ERR_CONNECT;
    }

    l206_write("AT+ZIPCALL=1\r\n");
    if(l206_wait_rsp("OK", 5)) {
        logi("%s: zipcall success", __func__);
        ret = TRUE;
    } else {
        loge("%s: zipcall gprs failed", __func__);
        ret = FALSE;
        goto ERR_CONNECT;
    }

    memset(tx_buf, 0x00, 128);
    snprintf((char *)tx_buf, 1024, "AT+ZIPOPEN=2,0,%s,%d\r\n",
            host, port);
    l206_write((char *)tx_buf);
    if(l206_wait_rsp("CONNECT OK", 10)) {
        logi("%s: connect %s:%d success", __func__,
                host, port);
        ret = TRUE;
    } else {
        loge("%s: connect %s:%d failed", __func__,
                host, port);
        ret = FALSE;
    }

ERR_CONNECT:

    return ret;
}

uint8_t l206_http_connect(const char *host, uint32_t port)
{
    uint8_t ret;
    uint8_t tx_buf[64];

    memset(tx_buf, 0x00, 64);
    comClearRxFifo(COM_L206);
    l206_write("AT+CSTT=\"unim2m.njm2mapn\"\r\n");
    if(l206_wait_rsp("OK", 3)) {
        logi("%s: cstt unim2m.njm2mapn success", __func__);
        ret = TRUE;
    } else {
        loge("%s: cstt unim2m.njm2mapn failed", __func__);
        ret = FALSE;
        goto ERROR;
    }

    comClearRxFifo(COM_L206);
    l206_write("AT+CIICR\r\n");
    if(l206_wait_rsp("OK", 3)) {
        logi("%s: ciicr success", __func__);
        ret = TRUE;
    } else {
        loge("%s: ciicr failed", __func__);
        ret = FALSE;
        goto ERROR;
    }

    comClearRxFifo(COM_L206);
    l206_write("AT+CIFSR\r\n");
    if(l206_wait_rsp("OK", 3)) {
        logi("%s: cifsr success", __func__);
        ret = TRUE;
    } else {
        loge("%s: cifsr failed", __func__);
        ret = FALSE;
        goto ERROR;
    }

    snprintf(tx_buf, 64, "AT+HTTPPARA=URL,\"%s\"\r\n", host);
    comClearRxFifo(COM_L206);
    l206_write(tx_buf);
    if(l206_wait_rsp("OK", 8)) {
        logi("%s: host '%s' success", __func__, host);
        ret = TRUE;
    } else {
        loge("%s: host '%s' failed", __func__, host);
        ret = FALSE;
        goto ERROR;
    }

    memset(tx_buf, 0x00, 64);
    snprintf(tx_buf, 64, "AT+HTTPPARA=PORT,%d\r\n", port);
    comClearRxFifo(COM_L206);
    l206_write(tx_buf);
    if(l206_wait_rsp("OK", 8)) {
        logi("%s: port '%d' success", __func__, port);
        ret = TRUE;
    } else {
        loge("%s: port '%d' failed", __func__, port);
        ret = FALSE;
        goto ERROR;
    }

    comClearRxFifo(COM_L206);
    l206_write("AT+HTTPSETUP\r\n");
    if(l206_wait_rsp("OK", 8)) {
        logi("%s: http setup success", __func__);
        ret = TRUE;
    } else {
        loge("%s: http setup failed", __func__);
        ret = FALSE;
        goto ERROR;
    }
ERROR:
    return ret;
}

void l206_http_get(void)
{
    char buf[512];

    memset(buf, 0x00, 512);
    comClearRxFifo(COM_L206);
    l206_write("AT+HTTPACTION=0\r\n");
    if(l206_read_rsp("close", buf, 10)) {
        logi("%s: http get success", __func__);
    } else {
        loge("%s: http get failed", __func__);
    }

    logi("buf: %s\n", buf);

}

void l206_write(char *buf)
{
    comClearRxFifo(COM_L206);
    comSendBuf(COM_L206, (uint8_t *)buf, strlen(buf));
    //logi("-> %s", buf);
}

uint8_t l206_is_connected(void)
{
    return mConnected;
}


uint8_t l206_wait_rsp(char *ack, uint16_t timeout)
{
    OS_ERR err;
    uint8_t u_data;
    uint16_t pos = 0;
    uint32_t len = 0;
    uint8_t ret;
    uint8_t u_rx_buf[100];

    mTimeoutFlag = 0;
    len = strlen(ack);
    l206_start_timer(timeout);
    //suspend receiver task
    if(mConnected) {
        OSTaskSuspend(&L206TaskTCB, &err);
    }
    while(1) {
        os_delay_ms(1);
        if(mTimeoutFlag == 1) {
            ret = 0;
            break;
        }
        if(comGetChar(COM_L206, &u_data)) {
            printf("%c", u_data);
            if(u_data == '\n') {
                //logi("rx_buf = %s, len = %d", u_rx_buf, len);
                if(pos > 0) {
                    if(memcmp(u_rx_buf, ack, len) == 0) {
                        ret = 1;
                        l206_stop_timer();
                        break;
                    } else {
                        pos = 0;
                    }
                } else {
                    pos = 0;
                }
            } else {
                if(pos < sizeof(u_rx_buf)) {
                    if(u_data >= ' ') {
                        u_rx_buf[pos ++] = u_data;
                    }
                }
            }
        }
    }
    //resume task
    if(mConnected) {
        OSTaskResume(&L206TaskTCB, &err);
    }
    return ret;
}

uint16_t l206_read_rsp(char *ack, char *data, uint16_t timeout)
{
    OS_ERR err;
    uint8_t u_data;
    uint16_t pos = 0;
    uint32_t len = 0;
    uint16_t ret;
    uint8_t u_rx_buf[100];

    mTimeoutFlag = 0;
    len = strlen(ack);
    l206_start_timer(timeout);
    //suspend receiver task
    if(mConnected) {
        OSTaskSuspend(&L206TaskTCB, &err);
    }
    while(1) {
        os_delay_ms(1);
        if(mTimeoutFlag == 1) {
            ret = 0;
            break;
        }
        if(comGetChar(COM_L206, &u_data)) {
            printf("%c", u_data);
            u_rx_buf[pos ++] = u_data;
            if(pos >= len) {
                if(memcmp(ack, u_rx_buf + (pos - len), len) == 0) {
                    ret = pos;
                    memcpy(data, u_rx_buf, pos);
                    l206_stop_timer();
                    break;
                }
            }
        }
    }
    //resume task
    if(mConnected) {
        OSTaskResume(&L206TaskTCB, &err);
    }
    return ret;
}

void l206_lock(void)
{
    OS_ERR err;

    OSMutexPend(&mMutex, 0, OS_OPT_PEND_BLOCKING, 0, &err);
}

void l206_unlock(void)
{
    OS_ERR err;

    OSMutexPost(&mMutex, OS_OPT_POST_NONE, &err);
}

char *l206_get_imei(void)
{
    return l206_imei;
}
